package com.yunyan.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.yunyan.entity.*;
import com.yunyan.mapper.PredictMapper;
import com.yunyan.req.PredictParam;
import com.yunyan.resp.FrontendPredict;
import com.yunyan.resp.JsonResp;
import com.yunyan.resp.PredictResp;
import com.yunyan.service.*;
import com.yunyan.util.predict.AdmissionRatio;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;

@Service
public class PredictServiceImpl extends ServiceImpl<PredictMapper, PredictData> implements PredictService {


    @Resource
    private RankService rankService;

    @Resource
    private HistoryService historyService;


    @Resource
    private SchoolService schoolService;


    @Resource
    private UserService userService;

    @Resource
    private RecommendService recommendService;

    @Override
    public Object simList(PredictParam predictParam) {
        List<SchoolData> schoolDataList = schoolService.list();

        Map<String, SchoolData> collect = schoolDataList.stream().collect(Collectors.toMap(SchoolData::getName, x -> x));

        List<PredictData> predictList = this.list();
        List<HistoryData> scoreList = historyService.list(new LambdaQueryWrapper<HistoryData>()
                .isNotNull(HistoryData::getLowerScore)
        );
        List<RankData> rankList = rankService.list(new LambdaQueryWrapper<>());

        Map<String, PredictData> predictMap = predictList.stream().collect
                (Collectors.toMap(x -> x.getCollegeName() + "," + x.getProfessorName() + "," + x.getSubject(), x -> x));

        Map<String, RankData> rankMap = rankList.stream().
                collect(Collectors.toMap(x -> x.getYear() + "," + x.getScore().replace("及以上", ""), x -> x));

        Map<String, RankData> topMap = new HashMap<>();
        List<RankData> R2021 = rankList.stream().filter(x -> x.getYear().equals(2021))
                .sorted(Comparator.comparingInt(x -> Integer.parseInt(x.getScore().replace("及以上", ""))))
                .collect(Collectors.toList());
        List<RankData> R2022 = rankList.stream().filter(x -> x.getYear().equals(2022))
                .sorted(Comparator.comparingInt(x -> Integer.parseInt(x.getScore().replace("及以上", ""))))
                .collect(Collectors.toList());
        List<RankData> R2023 = rankList.stream().filter(x -> x.getYear().equals(2023))
                .sorted(Comparator.comparingInt(x -> Integer.parseInt(x.getScore().replace("及以上", ""))))
                .collect(Collectors.toList());
        topMap.put("2021", R2021.get(0));
        topMap.put("2022", R2022.get(0));
        topMap.put("2023", R2023.get(0));

        Integer rank = predictParam.getRank();
        Integer score = predictParam.getScore();
        String subject = predictParam.getSubject();
        String city = predictParam.getCity();

        // 过滤文理科 预测的数据 和 真实的数据
        predictList = predictList.stream().filter(x -> x.getSubject().equals(subject)).collect(Collectors.toList());
        scoreList  = scoreList.stream().filter(x -> x.getSubject().equals(subject)).collect(Collectors.toList());

        Map<String, List<HistoryData>> professorData = scoreList.stream().collect(
                Collectors.groupingBy(x -> x.getCollegeName() + "," + x.getProfessorName() + "," + x.getSubject()));

        PredictData predictData = predictList.stream().sorted(Comparator.comparingInt(PredictData::getProfessorScore)).findFirst().get();
        List<PredictResp> result = new ArrayList<>();
        Map<String, Integer> LowerRankMap = new HashMap<>();
        for (Map.Entry<String, List<HistoryData>> entry : professorData.entrySet()) {

            if (!predictMap.containsKey(entry.getKey())) {
                continue;
            }

            PredictResp predictResp = new PredictResp();

            List<HistoryData> professorScoreList = entry.getValue();
            List<String> ranks = new ArrayList<>();
            for (HistoryData historyData : professorScoreList) {
                // 历史分数的排行
                RankData rankData = rankMap.get(historyData.getYear() + "," + historyData.getLowerScore());
                RankData topData = topMap.get(rankData.getYear().toString());
//                        rankList.stream().filter(x -> x.getYear().equals(rankData.getYear()))
//                        .sorted(Comparator.comparingInt(x -> Integer.parseInt(x.getScore().replace("及以上", ""))))
//                        .findFirst().get();
                if (subject.equals("理科")) {
                    ranks.add(Integer.parseInt(rankData.getPhyTotalNumber() ) + "," + topData.getPhyTotalNumber());
                } else {
                    if (rankData.getHisTotalNumber() != null) {
                        ranks.add(Integer.parseInt(rankData.getHisTotalNumber())  + "," + topData.getHisTotalNumber());
                    }
                }
            }


            // 排名
            ranks.add(predictMap.get(entry.getKey()).getProfessorRank() + "," + predictData.getProfessorRank() + "," + predictData.getSubject());
            ranks.sort((o1, o2) -> {
                String[] split = o1.split(",");
                String[] split1 = o2.split(",");
                return Integer.parseInt(split[0]) - Integer.parseInt(split1[0]);
            });

            // 计算 F1 F2
            String small = ranks.get(0);
            String big = ranks.get(ranks.size() - 1);
            String[] split = small.split(",");
            String[] split1 = big.split(",");
            double p = Double.parseDouble(split[0]) / Double.parseDouble(split[1]);
            // 分数预测结果
            double f1 = AdmissionRatio.calculateF1(predictMap.get(entry.getKey()).getProfessorScore(), score);
            // 排名预测结果
            double f2 = AdmissionRatio.calculateF2(Integer.parseInt(split1[0]), Integer.parseInt(split[0]), (int)(Integer.parseInt(split[0]) * 0.9), rank, p);
            //double ratio = 0.3 * f1 + 0.7 * f2;
            HistoryData record = professorScoreList.get(0);
            SchoolData schoolData = collect.get(record.getCollegeName());
            if (schoolData != null) {
                record.setSchoolData(schoolData);
                if (LowerRankMap.containsKey(schoolData.getName()) && LowerRankMap.get(schoolData.getName()) < Integer.parseInt(split[0])) {
                    LowerRankMap.put(schoolData.getName(), Integer.parseInt(split[0]));
                } else {
                    LowerRankMap.put(schoolData.getName(), Integer.parseInt(split[0]));
                }
            } else {
                continue;
            }
            predictResp.setProfessor(record);
            if (predictParam.getStandard().equals("按照分数预测")) {
                predictResp.setRatio(f1);
            } else {
                predictResp.setRatio(f2);
            }
            result.add(predictResp);
            if (!StringUtils.isEmpty(city)) {
                result = result.stream().filter(x -> x.getProfessor().getSchoolData().getAddress().contains(city)).collect(Collectors.toList());
            }
        }
        // 按照学校展示， 学校内层包含了各个专业的预测概率
        Map<SchoolData, List<PredictResp>> rest = result.stream().collect(Collectors.groupingBy(x -> x.getProfessor().getSchoolData()));
        List<FrontendPredict> list = new ArrayList<>();
        for (Map.Entry<SchoolData, List<PredictResp>> listEntry : rest.entrySet()) {
            FrontendPredict predict = new FrontendPredict();
            predict.setSchoolData(listEntry.getKey());
            predict.setProfessorsList(listEntry.getValue());
            list.add(predict);
        }

        for (FrontendPredict predict : list) {
            List<PredictResp> professorsList = predict.getProfessorsList();
            List<PredictResp> scoreSorted = professorsList.stream().sorted((x1, x2) -> {
                return x1.getProfessor().getLowerScore().compareTo(x2.getProfessor().getLowerScore());
            }).collect(Collectors.toList());
            predict.setLowerScore(Integer.parseInt(scoreSorted.get(0).getProfessor().getLowerScore()));
            predict.setLowerRank(LowerRankMap.get(predict.getSchoolData().getName()));
        }
        // 按照分数排行
        list.sort((o1, o2) -> o2.getLowerScore() - o1.getLowerScore());
        return list;

    }


    @Transactional
    @Override
    public Object recommend(String userId) {
        UserData userData = userService.getById(userId);
        if (userData.getRank() == null
                || userData.getScore() == null
                || userData.getSubject() == null
                || userData.getExamYear() == null
        )
        {
            return JsonResp.error("请完善个人分数和排名信息");
        }

            List<SchoolData> schoolDataList = schoolService.list();

            Map<String, SchoolData> collect = schoolDataList.stream().collect(Collectors.toMap(SchoolData::getName, x -> x));

            List<PredictData> predictList = this.list();
            List<HistoryData> scoreList = historyService.list(new LambdaQueryWrapper<HistoryData>()
                    .isNotNull(HistoryData::getLowerScore)
            );
            List<RankData> rankList = rankService.list(new LambdaQueryWrapper<>());

            Map<String, PredictData> predictMap = predictList.stream().collect
                    (Collectors.toMap(x -> x.getCollegeName() + "," + x.getProfessorName() + "," + x.getSubject(), x -> x));

            Map<String, RankData> rankMap = rankList.stream().
                    collect(Collectors.toMap(x -> x.getYear() + "," + x.getScore().replace("及以上", ""), x -> x));

            Map<String, RankData> topMap = new HashMap<>();
            List<RankData> R2021 = rankList.stream().filter(x -> x.getYear().equals(2021))
                    .sorted(Comparator.comparingInt(x -> Integer.parseInt(x.getScore().replace("及以上", ""))))
                    .collect(Collectors.toList());
            List<RankData> R2022 = rankList.stream().filter(x -> x.getYear().equals(2022))
                    .sorted(Comparator.comparingInt(x -> Integer.parseInt(x.getScore().replace("及以上", ""))))
                    .collect(Collectors.toList());
            List<RankData> R2023 = rankList.stream().filter(x -> x.getYear().equals(2023))
                    .sorted(Comparator.comparingInt(x -> Integer.parseInt(x.getScore().replace("及以上", ""))))
                    .collect(Collectors.toList());
            topMap.put("2021", R2021.get(0));
            topMap.put("2022", R2022.get(0));
            topMap.put("2023", R2023.get(0));

            Integer rank = userData.getRank();
            Integer score = userData.getScore();
            String subject = userData.getSubject();

            // 过滤文理科 预测的数据 和 真实的数据
            predictList = predictList.stream().filter(x -> x.getSubject().equals(subject)).collect(Collectors.toList());
            scoreList  = scoreList.stream().filter(x -> x.getSubject().equals(subject)).collect(Collectors.toList());

            Map<String, List<HistoryData>> professorData = scoreList.stream().collect(
                    Collectors.groupingBy(x -> x.getCollegeName() + "," + x.getProfessorName() + "," + x.getSubject()));

            PredictData predictData = predictList.stream().sorted(Comparator.comparingInt(PredictData::getProfessorScore)).findFirst().get();
            List<PredictResp> result = new ArrayList<>();
            Map<String, Integer> LowerRankMap = new HashMap<>();
            for (Map.Entry<String, List<HistoryData>> entry : professorData.entrySet()) {

                if (!predictMap.containsKey(entry.getKey())) {
                    continue;
                }

                PredictResp predictResp = new PredictResp();

                List<HistoryData> professorScoreList = entry.getValue();
                List<String> ranks = new ArrayList<>();
                for (HistoryData historyData : professorScoreList) {
                    // 历史分数的排行
                    RankData rankData = rankMap.get(historyData.getYear() + "," + historyData.getLowerScore());
                    RankData topData = topMap.get(rankData.getYear().toString());
//                        rankList.stream().filter(x -> x.getYear().equals(rankData.getYear()))
//                        .sorted(Comparator.comparingInt(x -> Integer.parseInt(x.getScore().replace("及以上", ""))))
//                        .findFirst().get();
                    if (subject.equals("理科")) {
                        ranks.add(Integer.parseInt(rankData.getPhyTotalNumber() ) + "," + topData.getPhyTotalNumber());
                    } else {
                        if (rankData.getHisTotalNumber() != null) {
                            ranks.add(Integer.parseInt(rankData.getHisTotalNumber())  + "," + topData.getHisTotalNumber());
                        }
                    }
                }


                // 排名
                ranks.add(predictMap.get(entry.getKey()).getProfessorRank() + "," + predictData.getProfessorRank() + "," + predictData.getSubject());
                ranks.sort((o1, o2) -> {
                    String[] split = o1.split(",");
                    String[] split1 = o2.split(",");
                    return Integer.parseInt(split[0]) - Integer.parseInt(split1[0]);
                });

                // 计算 F1 F2
                String small = ranks.get(0);
                String big = ranks.get(ranks.size() - 1);
                String[] split = small.split(",");
                String[] split1 = big.split(",");
                double p = Double.parseDouble(split[0]) / Double.parseDouble(split[1]);
                // 分数预测结果
                double f1 = AdmissionRatio.calculateF1(predictMap.get(entry.getKey()).getProfessorScore(), score);
                // 排名预测结果
                double f2 = AdmissionRatio.calculateF2(Integer.parseInt(split1[0]), Integer.parseInt(split[0]), (int)(Integer.parseInt(split[0]) * 0.9), rank, p);
                double ratio = 0.3 * f1 + 0.7 * f2;
                HistoryData record = professorScoreList.get(0);
                SchoolData schoolData = collect.get(record.getCollegeName());
                if (schoolData != null) {
                    record.setSchoolData(schoolData);
                    if (LowerRankMap.containsKey(schoolData.getName()) && LowerRankMap.get(schoolData.getName()) < Integer.parseInt(split[0])) {
                        LowerRankMap.put(schoolData.getName(), Integer.parseInt(split[0]));
                    } else {
                        LowerRankMap.put(schoolData.getName(), Integer.parseInt(split[0]));
                    }
                } else {
                    continue;
                }
                predictResp.setProfessor(record);
                predictResp.setRatio(ratio);
                result.add(predictResp);
            }

        List<PredictResp> middleList = result.stream().filter(x -> x.getRatio() >= 0.4 && x.getRatio() <= 0.7)
                .collect(Collectors.toList());

        List<PredictResp> possibleList = result.stream().filter(x -> x.getRatio() >= 0.7 && x.getRatio() <= 0.95)
                .collect(Collectors.toList());

        List<PredictResp> TopList = result.stream().filter(x -> x.getRatio() > 0.95)
                .collect(Collectors.toList());

        List<PredictResp> recommendList = new ArrayList<>();

        if (middleList.size() > 32) {
            recommendList.addAll(middleList.subList(0, 32));
        } else {
            recommendList.addAll(middleList);
        }

        if (possibleList.size() > 32) {
            recommendList.addAll(possibleList.subList(0, 32));
        } else {
            recommendList.addAll(possibleList);
        }

        if (TopList.size() > 32) {
            recommendList.addAll(TopList.subList(0, 32));
        } else {
            recommendList.addAll(TopList);
        }

        // 保存到数据库中
        List<RecommendData> recommendSavedData = recommendList.stream().map(x -> {
            RecommendData recommendData = new RecommendData();
            recommendData.setRatio(BigDecimal.valueOf(x.getRatio()));
            recommendData.setProfessorName(x.getProfessor().getProfessorName());
            recommendData.setSchoolName(x.getProfessor().getSchoolData().getName());
            recommendData.setUserId(userId);
            return recommendData;
        }).collect(Collectors.toList());
        // 清除以前的预测
        recommendService.remove(new LambdaQueryWrapper<RecommendData>()
                .eq(RecommendData::getUserId, userId)
        );
        // 保存新的预测
        recommendService.saveBatch(recommendSavedData);
        return recommendList;
    }
}
